/*
 * ULSF Sagittarius IDA
 * Copyright (C) 2012, John Pritchard, all rights reserved.
 * 
 * This program is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see
 * <http://www.gnu.org/licenses/>.
 */
package sagittarius.airfoil;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * <b> Temporary place holder compiles but has two major bugs</b>
 * 
 * <h3>BUGS</h3>
 * <ul>
 * 
 * <li> Review vertices indeces for (*2) from panel x,y indeces</li>
 * 
 * <li> Review vertices counts from npdim to npoints</li>
 * 
 * </ul>
 */
public class UIUC
    extends Airfoil
{

    public final static String UIUC_ADB_URL = "http://www.ae.illinois.edu/m-selig/ads/coord/";
    /**
     * @param string A URL or file name for UIUC ACDB
     * @return A URL for the UIUC ACDB
     */
    public final static URL For(String string)
        throws MalformedURLException
    {
        if (-1 != string.indexOf(':'))
            return new URL(string);
        else {
            if (!string.endsWith(".dat")){
                string += ".dat";
            }
            {
                File test = new File(string);
                if (test.exists() && test.isFile())
                    return test.toURI().toURL();
                else
                    return new URL(UIUC_ADB_URL+string);
            }
        }
    }
    /**
     * UIUC ACDB file formats.
     */
    public static enum Format {
        Selig, Lednicer;

        public final static class Point {

            public final float x, y;

            public Point(UIUC.Format.Reader r)
                throws IOException
            {
                super();
                String line = r.readLine();
                if (null == line)
                    throw new java.io.EOFException();
                else if (0 == line.length())
                    throw new IllegalStateException();                    
                else {
                    line = line.trim();
                    int idx = line.indexOf(' ');
                    if (-1 == idx)
                        throw new IllegalStateException();
                    else {
                        this.x = Float.parseFloat(line.substring(0,idx));
                        this.y = Float.parseFloat(line.substring(idx+1).trim());
                    }
                }
            }
        }
        /**
         * Depends on the first character of the Lednicer format being
         * a space (0x20) character.
         */
        public final static class Reader
            extends BufferedReader
        {
            public final String title;
            public final Format format;
            public final int lednicerUpper, lednicerLower;

            public Reader(java.io.Reader in)
                throws IOException
            {
                super(in);
                String title = this.readLine();
                if (null == title)
                    throw new java.io.EOFException();
                else if (0 == title.length())
                    throw new IOException("File format error, missing title.");
                else if (' ' == title.charAt(0)){
                    this.format = Lednicer;
                    this.title = title.trim();
                    UIUC.Format.Point p = this.readPoint();
                    this.lednicerUpper = (int)p.x;
                    this.lednicerLower = (int)p.y;
                }
                else {
                    this.format = Selig;
                    this.title = title;
                    this.lednicerUpper = this.lednicerLower = 0;
                }
            }

            public UIUC.Format.Point readPoint()
                throws IOException
            {
                return (new UIUC.Format.Point(this));
            }
        }

    }



    public final int np;

    private final float[] vertices;


    public UIUC(int np, URL url)
        throws IOException, NumberFormatException
    {
        super(np);
        /*
         * Splines
         */
        float[] s1 = new float[this.npdim];
        float[] s2 = new float[this.npdim];
        float[] xp = new float[this.npdim];
        float[] yp = new float[this.npdim];
        /*
         * Section
         */
        float[] xb = new float[this.npdim];
        float[] yb = new float[this.npdim];
        float[] xm = new float[this.npdim-1];
        float[] ym = new float[this.npdim-1];

        int nle1 = 0, len = 0;

        UIUC.Format.Reader in = null;
        {
            in = new UIUC.Format.Reader(new InputStreamReader(url.openStream(),"US-ASCII"));

            int nix = 0;
            try {
                float xle = 1.0f;
                /*
                 * Temporarily store the coordinates in the mid-point arrays.
                 * They'll be moved to the body arrays when the points are
                 * redistributed.
                 */
                while (true){
                    try {
                        UIUC.Format.Point r = in.readPoint();

                        xm[nix] = r.x;
                        ym[nix] = r.y;

                        if (xm[nix] < xle){
                            xle = xm[nix];
                            nle1 = nix;
                        }
                        nix += 1;
                    }
                    catch (IllegalStateException drop){
                    }
                }
            }
            catch (java.io.EOFException done){
            }
            finally {
                if (null != in){
                    try {
                        in.close();
                    }
                    catch (Exception ignore){
                    }
                }
            }
            len = nix;
        }
        this.np = len;
        /*
         * Convert UIUC formats to the internal order
         */
        if (UIUC.Format.Lednicer == in.format){
            if (len == (in.lednicerUpper + in.lednicerLower)){
                /*
                 * Transpose format "Lednicer" to format "Selig" by
                 * reversing the order of the first half (upper
                 * surface) of the series.
                 */
                float[] xl = new float[in.lednicerUpper];
                float[] yl = new float[in.lednicerUpper];
                final int lu = in.lednicerUpper, lu1 = (lu-1);
                for (int ix = lu1; -1 < ix; ix--){
                    xl[lu1-ix] = xm[ix];
                    yl[lu1-ix] = ym[ix];
                }
                System.arraycopy(xl,0,xm,0,lu);
                System.arraycopy(yl,0,ym,0,lu);
            }
            else
                throw new IllegalStateException("Format 'Lednicer' error in data count.");
        }
        /*
         * Transpose the UIUC structure (upper then lower) to the
         * structure employed here (lower then upper).
         */
        {
            float[] xl = new float[len];
            float[] yl = new float[len];
            final int z = (len-1);
            for (int ix = z; -1 < ix; ix--){
                xl[z-ix] = xm[ix];
                yl[z-ix] = ym[ix];
            }
            System.arraycopy(xl,0,xm,0,len);
            System.arraycopy(yl,0,ym,0,len);
        }

        /*
         * Load the original arc-lengths for spline. Locate the LE.
         */
        s1[0] = 0.0f;
        nle1 = 0;
        {
            float dx1, dy1, ds;
            for (int nix = 1; nix < len; nix++){
                dx1 = (xm[nix] - xm[nix-1]);
                dy1 = (ym[nix] - ym[nix-1]);
                ds = (float)Math.sqrt( dx1*dx1 + dy1*dy1);
                s1[nix] = (s1[nix-1] + ds);
                /*
                 * For sequence X having values
                 * {x0,...,0.0,0.0,...,xn} (double bottom in lednicer
                 * data sets) select the second as the start of the
                 * upper surface from the leading edge
                 */
                if (xm[nix] <= xm[nix-1]){
                    nle1 = nix;
                }
            }
        }

        /*
         * Generate splines.
         */
        Airfoil.Spline(s1,xm,this.np,1.0e30f,1.0e30f,xp);
        Airfoil.Spline(s1,ym,this.np,1.0e30f,1.0e30f,yp);

        /*
         * Compute the leading/trailing-edge spacing.
         */
        final float dsle = (float)Math.pow((1.0f / this.npoints),2);
        /*
         * Lower surface
         */
        Airfoil.Redistribute(s1,0,nle1,s2,0,this.nle,dsle);
        /*
         * Upper surface
         */
        Airfoil.Redistribute(s1,nle1,(this.np-1),s2,this.nle,(this.npoints-1),dsle);

        for (int nix = 0; nix < this.npoints; nix++){
            xb[nix] = Airfoil.Splint(s1,xm,xp,this.np,s2[nix]);
            yb[nix] = Airfoil.Splint(s1,ym,yp,this.np,s2[nix]);
        }

        xb[0] = xm[0];
        yb[0] = ym[0];
        xb[this.nle] = xm[nle1];
        yb[this.nle] = ym[nle1];
        xb[this.npoints-1] = xm[this.np-1];
        yb[this.npoints-1] = ym[this.np-1];

        float[] vertices = new float[this.npoints*2];

        for (int xbx = 0, ybx = 0, vx = 0, vy = 1, count = vertices.length; vx < count; xbx++, ybx++, vx += 2, vy += 2){
            vertices[vx] = xb[xbx];
            vertices[vy] = yb[ybx];
        }
        this.vertices = vertices;
    }


    public float[] getVertices(){
        return this.vertices.clone();
    }
}
